package com.Lyoto.core.config.aspect;

import com.Lyoto.core.enetity.annotation.RateLimit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

/**
 * @author Lyoto
 * @Description
 * @Date 2022-09-22 9:21
 * @Version
 **/
@Aspect
@Configuration
public class LimitAspect {
    @Autowired
    @Qualifier("customRedisTemplate")
    private RedisTemplate<String, Serializable> redisTemplate;
    
    @Autowired
    private DefaultRedisScript<Number> redisScript;
    
    @Around("execution(* com.Lyoto.core.controller ..*(..) )")
    public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> declaringClass = method.getDeclaringClass();
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        if (rateLimit != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ipAddress = getIpAddr(request);
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(ipAddress).append("-").append(declaringClass.getName()).append("-").append(method.getName()).append("-").append(rateLimit.key());
            List<String> keys = Collections.singletonList(stringBuffer.toString());
            Number number = redisTemplate.execute(redisScript, keys, rateLimit.count(), rateLimit.time());
            if (number != null && number.intValue() != 0 && number.intValue() <= rateLimit.count()) {
                return joinPoint.proceed();
            }
        }else{
            return joinPoint.proceed();
        }
        throw new RuntimeException("访问频率过快啦！");
        
    }
    
    private String getIpAddr( HttpServletRequest request) {
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0|| "unknown".equalsIgnoreCase(ip)){
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 ||"unknown".equalsIgnoreCase(ip)){
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 ||"unknown".equalsIgnoreCase(ip)){
                ip = request.getRemoteAddr();
            }
            if (ip!=null&& ip.length()> 15) {
                if (ip.indexOf(",")>0) {
                    ip = ip.substring(0,ip.indexOf(","));
                }
            }
        }catch (Exception e){
            ip = "";
        }
        return ip;
    }
    
}
